#include "log.hpp"
#include "add.hpp"
#include <memory>

class Socket;
const int default_socket = -1;
const int sock_listen = 16;

using socket_ptr = shared_ptr<Socket>;

class Socket
{
public:
    virtual void Create_socket() = 0;
    virtual void Bind_socket(const add_in) = 0;
    virtual void Listen_socket() = 0;
    virtual int Accept_socket(sockaddr_in *) = 0;
    virtual bool Connect_socket(add_in) = 0;
    virtual int get_sockfd() = 0;
    virtual int Recv(string *str) = 0;
    virtual int Send(const string &str) = 0;
    virtual bool Close() = 0;
public:
    void Bulid_Server(const add_in &addr)
    {
        Create_socket();
        Bind_socket(addr);
        Listen_socket();
    }

    void Bulid_Clinet(const add_in &addr)
    {
        Create_socket();
        Connect_socket(addr);
    }
};

class Tcp_socket : public Socket
{
public:
    Tcp_socket(int _fd = default_socket) : _socket(_fd)
    {
    }

    void Create_socket() override
    {
        _socket = socket(AF_INET, SOCK_STREAM, 0);
        if (_socket < 0)
        {
            log(FATAL, "socket create error : %d", _socket);
            exit(SOCKET_ERROR);
        }
        log(DEBUG, "sock create success: %d", _socket);
    }

    void Bind_socket(add_in addr) override
    {
        Sock_bind_success(); // 设置反复绑定端口

        sockaddr_in addr_in;
        memset(&addr_in, 0, sizeof(addr_in));
        addr_in.sin_family = AF_INET;
        addr_in.sin_addr.s_addr = inet_addr(addr.get_ip().c_str());
        addr_in.sin_port = htons(addr.get_port());

        int n = bind(_socket, (sockaddr *)&addr_in, sizeof(addr_in));
        if (n < 0)
        {
            log(FATAL, "bind error");
            exit(BIND_ERROR);
        }
        log(DEBUG, "bind success");
    }

    void Listen_socket() override
    {
        int n = listen(_socket, sock_listen);
        if (n < 0)
        {
            log(FATAL, "listen error");
            exit(BIND_ERROR);
        }
        log(DEBUG, "listen success");
    }

    int Accept_socket(sockaddr_in *addr) override
    {
        socklen_t len = sizeof(addr);
        int n = accept(_socket, (sockaddr *)addr, &len);
        if (n < 0)
        {
            log(WARNING, "accept error");
            exit(ACCEPT_ERROR);
        }
        log(DEBUG, "accept success");
        return n;
    }

    int get_sockfd() override
    {
        return _socket;
    }

    bool Connect_socket(add_in addr) override // 连接
    {
        // 构建对面的addr
        sockaddr_in sock_addr;
        sock_addr.sin_family = AF_INET;
        sock_addr.sin_addr.s_addr = inet_addr(addr.get_ip().c_str());
        sock_addr.sin_port = htons(addr.get_port());

        int n = connect(_socket, (sockaddr *)&sock_addr, sizeof(sock_addr));
        if (n < 0)
        {
            log(DEBUG, "connect error");
            return false;
        }

        log(DEBUG, "connect success");
        return true;
    }

    int Recv(string *str) // 接受
    {
        char arr[1024];
        int n = recv(_socket, arr, sizeof(arr) - 1, 0);
        if (n > 0)
        {
            arr[n] = 0;
            *str = arr;
        }
        return n;
    }

    int Send(const string &str)
    {
        int n = send(_socket, str.c_str(), str.size(), 0);
        return n;
    }

    bool Close() override
    {
        if (_socket != -1)
        {
            close(_socket);
            return true;
        }
        return false;
    }

    void Sock_bind_success()
    {
        int set = 1;
        log(DEBUG, "Sock_bind_success");
        setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &set, sizeof(set));
    }

private:
    int _socket;
};
